import java.io.IOException;
import java.util.ArrayList;

public class Controlador {
	
	private Juego juego;
	private Consola consola;
	
	//CONSTRUCTOR
	public Controlador(){
		this.consola = new Consola();		
	}
	
	public void run(){		
		//carga los jugadores al juego
		this.cargarJugadores();
		
		do {
			// avanza de jugador
			this.juego.siguienteJugador();

			// ejecuta el turno
			this.turno();
		// mientras no haya terminado la partida
		} while (!this.juego.terminoPartida());
		
		//finaliza el juego
		this.juego.terminarJuego();
		
		//informa los resultados
		this.informarResultados();				
	}
	
	private void cargarJugadores(){
		
		//crea la lista que va a contener los jugadores
		ArrayList<String> nombresDeJugadores = new ArrayList<String>();
		//muestra la vienvenida y pide los nombres de jugadores
		consola.mostrarBienvenida();
		nombresDeJugadores = consola.pedirPalabra();
		
		//si la cantidad de jugadores ingresados no es correcta
		while(!(2 <= nombresDeJugadores.size() && nombresDeJugadores.size()<= 4)){
			//nombresDeJugadores.clear();
			consola.escribir("Ha ingresado cantidad de participantes incorrectos por favor vuelva a ingresar los nombres, recuerde que el minimo son dos jugadores y como maximo cuatro.");
			nombresDeJugadores = consola.pedirPalabra();			
		}
		
		//limpia la pantalla
		consola.limpiarPantalla();
		
		//inicia el juego con la lista de jugadores
		this.juego = new Juego(nombresDeJugadores);
	}
	
	private void turno() {
		
		if(this.juego.getNumEtapa() == 1){						
			this.primerEtapa();
			this.juego.avanzarEtapa();						
			this.consola.limpiarPantalla();
		}
			
		if(this.juego.getNumEtapa() == 2){ 
			this.segundaEtapa();
			this.juego.avanzarEtapa();
			this.consola.limpiarPantalla();			
		}
		
		
		if(this.juego.getNumEtapa() == 3 && this.juego.puedeCerrarCamino()){ 			
			this.terceraEtapa();			
		}
		
		//cierra el turno del jugador actual
		this.juego.cerrarTurno();		
	}
	
	private void informarResultados(){
		this.consola.escribir("GANADOR: ");
		this.consola.escribir(this.juego.getGanador().getNombre() + " - puntos: " + this.juego.getGanador().getPuntos());
		
		for(Jugador j : this.juego.getPerdedores()){
			this.consola.escribir(j.getNombre() + " - puntos: " + j.getPuntos());
		}
	}
	
	private void primerEtapa() {				
		
		//muestra la info de la etapa
		this.mostrarInfoEtapa();	
		
		//menu previo a comenzar la etapa
		this.menuPreEtapa(1);
		
		//muestra la cuadrilla
		this.consola.escribir("Cuadrilla");
		this.consola.mostrarCartas(this.juego.getCuadrilla());
		
		//verifica si se puede utilizar algun ayudante al principio del turno
		this.verificarUsoAyudante(1,true);
		
		//levanta una carta siempre y cuando sea posible
		this.levantarCarta();
		
		//verifica si se puede utilizar algun ayudante al final del turno
		this.verificarUsoAyudante(1,false);			
	}
	
	private void segundaEtapa() {
		
		//muestra la info de la etapa
		this.mostrarInfoEtapa();
		
		//menu previo a comenzar la etapa
		this.menuPreEtapa(2);
		
		//agrega alguna carta al camino siempre y cuando sea posible
		this.agregarCarta();
		
		//verifica si se puede utilizar algun ayudante al final del turno
		verificarUsoAyudante(2,false);
	}
	
	private void terceraEtapa() {
		
		//muestra la info de la etapa
		this.mostrarInfoEtapa();	
		
		//menu previo a comenzar la etapa
		this.menuPreEtapa(3);
		
		//intenta cerrar el camino siempre y cuando sea posible y el jugador lo desee
		this.cerrarCamino();
					
	}
	
	private void mostrarInfoEtapa(){
		//muestra el jugador que le toca el turno
		this.consola.escribir("turno: " + this.juego.getNombre());
		
		//muestra el numero de etapa
		this.consola.escribir("etapa numero: " + this.juego.getNumEtapa());			
	}
	
	private void menuPreEtapa(int etapa){
						
		int opcion = 0;
		boolean error = false;
		do{
			//muestra menu de opciones
			this.consola.mostrarMenuEtapa(etapa);
			
			error = false;
			//pedir opcion
			try{
				opcion = this.consola.pedirOpcion();
			}catch(IOException e){
				//opcion = 0;
				this.consola.escribir("Opcion incorrecta");
				error = true;
				continue;
				//this.consola.clearBuffer();
			}
			
			if(opcion != 1 && opcion != 2 && opcion != 3){
				this.consola.escribir("Opcion incorrecta");
				error = true;
			}
			
			if(!error){
				if(opcion == 2){
					//muestra el mapa
					this.consola.mostrarMapa(this.juego.getRegiones(), this.juego.getRutas());
					this.stop();
				}else if(opcion == 3){
					//muestra la informacion actual del jugador
					this.mostrarInfoActual();
					this.stop();
				}								
			}
		}while(error || opcion == 2 || opcion == 3);
		
		this.consola.limpiarPantalla();
	}
	
	private void levantarCarta(){
		
		this.consola.escribir("Seleccione alguna de las siguientes opciones: ");
		
		//muestra la cuadrilla
		consola.mostrarCartas(this.juego.getCuadrilla());

		//mostrar menu con opciones		
		consola.mostrarMenuLevantarCarta();						
		
		int opcion = 0;
		boolean error = false;
		do{
			error = false;
			//pedir opcion
			try{
				opcion = this.consola.pedirOpcion();
			}catch(IOException e){
				this.consola.escribir("Opcion incorrecta");
				error = true;
				continue;
			}
			
			if((opcion < 0 || opcion > 5) && opcion != 10){
				this.consola.escribir("Opcion incorrecta");
				error = true;
			}
		}while(error);
		
		if(opcion >= 0 && opcion <= 5){
			//levanta la carta de la cuadrilla
			this.juego.levantarCartaCuadrilla(opcion);
		}else{
			//levanta la carta del mazo
			this.juego.levantarCartaMazo();			
		}					
	}
	
	private void agregarCarta(){
		
		//muestra la info del camino y de la mano
		this.consola.escribir("Camino");
		this.consola.mostrarCartas(this.juego.verCamino());
		this.consola.escribir("");
		this.consola.escribir("Eliga alguna de las cartas de la mano o bien pase y perdera el camino actual");
		this.consola.mostrarCartas(this.juego.verMano());
		this.consola.mostrarMenuAgregarCarta();
		
		int opcion = 0;					
		boolean error = false;
			
		do{
			error = false;
			
			//pedir opcion
			try{
				opcion = this.consola.pedirOpcion();
			}catch(IOException e){
				this.consola.escribir("Opcion incorrecta");
				error = true;
				continue;
			}		
						
			//si es 10 se considera que el jugador pasa sin realizar acciones			
			if(opcion == 10){	
				this.juego.descartarCamino();
			//agrega la carta al camino			
			}else if(this.juego.tieneCarta(opcion)){
				try {
					//agrega la carta al camino siempre y cuando sea posible
					this.juego.agregarAlCamino(opcion);
				} catch (JuegoException e) {
					this.consola.escribir(e.getMessage());
					error = true;
				}			
			}else{
				this.consola.escribir("Opcion incorrecta");
				error = true;
			}
		}while(error);			
	}
	
	private void cerrarCamino(){
		
		//muestra menu
		this.consola.mostrarMenuCerrarCamino();
		
		int opcion = 0;
		boolean error = false;
		
		do{						
			error = false;
			
			//pedir opcion
			try{
				opcion = this.consola.pedirOpcion();
			}catch(IOException e){
				this.consola.escribir("Opcion incorrecta");
				error = true;
				continue;
			}		
			
			if(opcion != 1 && opcion != 2){
				error = true;
				this.consola.escribir("Opcion incorrecta");
			}
		}while(error);
		
		//si es 2 se considera que el jugador pasa sin realizar acciones
		if(opcion == 2){
			//pasa el turno
			this.juego.pasarTurno();			
		}else{			
			//si es camino simple se intenta cerrarlo
			if(this.juego.caminoUniRegion()){
				try{
					//cierra el camino
					this.juego.cerrarCaminoSimple();
				}catch(Exception e){
					this.consola.escribir(e.getMessage());
					this.stop();
				}
			//si es camino compuesto
			}else{
				//muestra las cartas del camino
				this.consola.escribir("camino");						
				this.consola.mostrarCartas(this.juego.verCamino());	
				this.consola.escribir("Ingrese los numeros de las cartas separados por espacios, luego presione enter");
				
				do{
					error = false;
					ArrayList<Integer> posiciones = new ArrayList<Integer>();
					
					//lee las opciones ingresadas
					for(String s : this.consola.pedirPalabra()){
						try{
							posiciones.add(Integer.parseInt(s));
						}catch(Exception e){
							this.consola.escribir("opciones mal ingresadas, reintente");
							error = true;
						}
					}
					
					//si no hubo error y se eligieron las cartas correspondientes
					if(!error){
						try{
							//se intenta cerrar el camino compuesto
							this.juego.cerrarCaminoCompuesto(posiciones);
						}catch(Exception e){
							this.consola.escribir(e.getMessage());
							this.stop();
						}
					}
				}while(!error);
				
			}
		}
	}
	
	//muestra los datos actuales del jugador
	private void mostrarInfoActual(){
		this.consola.escribir("Jugador: " + this.juego.getNombre());		
		
		this.consola.escribir("Fichas");
		this.consola.mostrarFichas(this.juego.getFichas());
		
		this.consola.escribir("Puntos: " + this.juego.getPuntos());		
		
		this.consola.escribir("Casillas restantes: " + this.juego.getCasillasRestantes());		
		
		this.consola.escribir("Status en mapa: " + this.juego.getStatusCasillas() + " casillas");		
		
		this.consola.escribir("Mano");
		//muestra las cartas que tiene en la mano
		this.consola.mostrarCartas(this.juego.verMano());	
			
		this.consola.escribir("Camino");
		//muestra las cartas que tiene en la mano
		this.consola.mostrarCartas(this.juego.verCamino());			
		
		this.consola.escribir("Ayudante utilizado: " + this.juego.getAyudanteUsado());
	}
	
	private void stop(){
		this.consola.escribir("Presione una tecla para continuar");
		this.consola.clearBuffer();
		this.consola.limpiarPantalla();
	}
	
	private void verificarUsoAyudante(int etapa, boolean principio){
		//verifica si el jugador no ha solicitado ayuda todavia
		if(!this.juego.pidioAyuda()){
			//trae los personajes disponibles para la situacion del jugador
			ArrayList<Personaje> personajes = this.juego.getPersonajes(etapa, principio);
			//si encontro personajes
			if(!personajes.isEmpty()){				
				
				this.consola.escribir("");
				
				//muestra los personajes
				this.consola.mostrarMenuPersonajes(personajes);				
				
				int opcionPedida = 0;
				boolean error = false;
				do{
					error = false;
					try{
						opcionPedida = consola.pedirOpcion();
					}catch(Exception e){
						this.consola.escribir("Opcion incorrecta");
						error = true;
						continue;
					}
					
					if((opcionPedida < 0 || opcionPedida >= personajes.size()) && opcionPedida != 10){
						this.consola.escribir("Opcion incorrecta");
						error = true;
					}
				}while(error);
				
				//si la opcion es valida y el jugador eligio algun personaje en particular
				if(opcionPedida != 10){
					//trae el personaje y realiza la accion segun corresponda
					Personaje pj = personajes.get(opcionPedida);
					if(pj.getTipo() == 'c'){
						this.levantarCarta();
					}else if(pj.getTipo() == 'a'){
						this.juego.renovarCuadrilla();					
					}else if(pj.getTipo() == 'p'){
						this.agregarCarta();
					}
					
					//marca al jugador como que utilizo la ayuda
					this.juego.usarAyuda(pj);
				}
			}	
		}
		
		this.consola.limpiarPantalla();
	}
	
}
